#define MONKEYLUA_OBJECT_MT "monkeylua_object_mt"

// other wrapper classes
class ml_Pointer : public Object
{
public:
	void *_voidstar; // which = 1
	const void *_constvoidstar; // which = 2
	int _which; // which = 0 means no pointer
	ml_Pointer() : _voidstar(NULL), _constvoidstar(NULL), _which(0) {};
	ml_Pointer(void *ptr) : _voidstar(ptr), _constvoidstar(NULL), _which(1) {};
	ml_Pointer(const void *ptr, int foo) : _voidstar(NULL), _constvoidstar(ptr), _which(2) {};
};

// lua struct wrapper classes
class ml_lua_Alloc : public Object
{
public:
	lua_Alloc _data;
};

class ml_lua_State : public Object
{
public:
	lua_State *_data;
	ml_lua_State() : _data(NULL) {};
	ml_lua_State(lua_State *L) : _data(L) {};
	
	bool Equals(ml_lua_State *other) { return other->_data==_data; }
};

class ml_luaL_Buffer : public Object
{
public:
	luaL_Buffer *_data;
};

class ml_MonkeyFunction : public Object
{
public:
	virtual int ml_Func(ml_lua_State *L) { return 0; }
};

// function wrappers
class ml_MonkeyLua : public Object
{
public:
	// atpanic -- requires lua_CFunction
	void ml_lua_call (ml_lua_State *L, int nargs, int nresults);
	int  ml_lua_checkstack (ml_lua_State *L, int extra);
	void ml_lua_close(ml_lua_State *L);
	void ml_lua_concat (ml_lua_State *L, int n);
	// cpcall -- requires lua_CFunction
	void ml_lua_createtable (ml_lua_State *L, int narr, int nrec);
	// dump -- requires lua_Writer
	int  ml_lua_equal (ml_lua_State *L, int index1, int index2);
	int  ml_lua_error (ml_lua_State *L);
	int  ml_lua_gc (ml_lua_State *L, int what, int data);
	ml_lua_Alloc *ml_lua_getallocf_rv (ml_lua_State *L, ml_Pointer *ud, ml_lua_Alloc *rv);
	ml_lua_Alloc *ml_lua_getallocf (ml_lua_State *L, ml_Pointer *ud);
	void ml_lua_getfenv (ml_lua_State *L, int index);
	void ml_lua_getfield (ml_lua_State *L, int index, String k);
	void ml_lua_getglobal (ml_lua_State *L, String name);
	// gethook -- requires lua_Hook
	// gethookcount -- not much point without gethook just yet
	// gethookmask -- not much point without gethook just yet
	// getinfo -- requires lua_Debug
	// getlocal -- requires lua_Debug
	int  ml_lua_getmetatable (ml_lua_State *L, int index);
	// getstack -- requires lua_Debug
	void ml_lua_gettable (ml_lua_State *L, int index);
	int  ml_lua_gettop (ml_lua_State *L);
	String ml_lua_getupvalue (ml_lua_State *L, int funcindex, int n);
	void ml_lua_insert (ml_lua_State *L, int index);
	int  ml_lua_isboolean (ml_lua_State *L, int index);
	int  ml_lua_iscfunction (ml_lua_State *L, int index);
	int  ml_lua_isfunction (ml_lua_State *L, int index);
	int  ml_lua_islightuserdata(ml_lua_State *L, int index);
	int  ml_lua_isnil (ml_lua_State *L, int index);
	int  ml_lua_isnone (ml_lua_State *L, int index);
	int  ml_lua_isnoneornil (ml_lua_State *L, int index);
	int  ml_lua_isnumber (ml_lua_State *L, int index);
	int  ml_lua_isstring (ml_lua_State *L, int index);
	int  ml_lua_istable (ml_lua_State *L, int index);
	int  ml_lua_isthread (ml_lua_State *L, int index);
	int  ml_lua_isuserdata (ml_lua_State *L, int index);
	int  ml_lua_lessthan (ml_lua_State *L, int index1, int index2);
	// load -- requires lua_Reader
	ml_lua_State *ml_lua_newstate_rv(ml_lua_Alloc *f, ml_Pointer *ud, ml_lua_State *rv);
	ml_lua_State *ml_lua_newstate(ml_lua_Alloc *f, ml_Pointer *ud);
	void ml_lua_newtable (ml_lua_State *L);
	ml_lua_State *ml_lua_newthread_rv (ml_lua_State *L, ml_lua_State *rv);
	ml_lua_State *ml_lua_newthread (ml_lua_State *L);
	ml_Pointer *ml_lua_newuserdata (ml_lua_State *L, int size);
	ml_Pointer *ml_lua_newuserdata_rv (ml_lua_State *L, int size, ml_Pointer *rv);
	int  ml_lua_next (ml_lua_State *L, int index);
	int  ml_lua_objlen (ml_lua_State *L, int index);
	int  ml_lua_pcall (ml_lua_State *L, int nargs, int nresults, int msgh);
	void ml_lua_pop (ml_lua_State *L, int n);
	void ml_lua_pushboolean (ml_lua_State *L, int b);
	// pushcclosure -- requires lua_CFunction
	// pushcfunction -- requires lua_CFunction
	// pushfstring -- requires varargs
	void ml_lua_pushinteger (ml_lua_State *L, int n);
	void ml_lua_pushlightuserdata (ml_lua_State *L, ml_Pointer *p);
	String ml_lua_pushliteral (ml_lua_State *L, String s);
	void ml_lua_pushlstring (ml_lua_State *L, String s, int len);
	void ml_lua_pushnil (ml_lua_State *L);
	void ml_lua_pushnumber (ml_lua_State *L, float n);
	void ml_lua_pushstring (ml_lua_State *L, String s);
	int  ml_lua_pushthread (ml_lua_State *L);
	void ml_lua_pushvalue (ml_lua_State *L, int index);
	// pushvfstring -- requires varargs
	int  ml_lua_rawequal (ml_lua_State *L, int index1, int index2);
	void ml_lua_rawget (ml_lua_State *L, int index);
	void ml_lua_rawgeti (ml_lua_State *L, int index, int n);
	void ml_lua_rawset (ml_lua_State *L, int index);
	void ml_lua_rawseti (ml_lua_State *L, int index, int n);
	// register -- requires lua_CFunction
	void ml_lua_remove (ml_lua_State *L, int index);
	void ml_lua_replace (ml_lua_State *L, int index);
	int  ml_lua_resume (ml_lua_State *L, int narg);
	void ml_lua_setallocf (ml_lua_State *L, ml_lua_Alloc *f, ml_Pointer *ud);
	int  ml_lua_setfenv (ml_lua_State *L, int index);
	void ml_lua_setfield (ml_lua_State *L, int index, String k);
	void ml_lua_setglobal (ml_lua_State *L, String name);
	// sethook -- requires lua_Hook
	// setlocal -- requires lua_Debug
	void ml_lua_setmetatable (ml_lua_State *L, int index);
	void ml_lua_settable (ml_lua_State *L, int index);
	void ml_lua_settop (ml_lua_State *L, int n);
	String ml_lua_setupvalue (ml_lua_State *L, int funcindex, int n);
	int  ml_lua_status (ml_lua_State *L);
	int  ml_lua_toboolean (ml_lua_State *L, int index);
	// tocfunction -- requires lua_CFunction
	int  ml_lua_tointeger (ml_lua_State *L, int index);
	// tolstring -- needs a pointer and is pointless in Monkey
	float ml_lua_tonumber (ml_lua_State *L, int index);
	ml_Pointer *ml_lua_topointer (ml_lua_State *L, int index);
	String ml_lua_tostring (ml_lua_State *L, int index);
	ml_lua_State *ml_lua_tothread_rv (ml_lua_State *L, int index, ml_lua_State *rv);
	ml_lua_State *ml_lua_tothread (ml_lua_State *L, int index);
	int  ml_lua_type (ml_lua_State *L, int index);
	String ml_lua_typename (ml_lua_State *L, int tp);
	void ml_lua_xmove (ml_lua_State *from, ml_lua_State *to, int n);
	int  ml_lua_yield (ml_lua_State *L, int nresults);
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	void ml_luaL_addchar (ml_luaL_Buffer *B, int c);
	void ml_luaL_addlstring (ml_luaL_Buffer *B, String s, int l);
	void ml_luaL_addsize (ml_luaL_Buffer *B, int n);
	void ml_luaL_addstring (ml_luaL_Buffer *B, String s);
	void ml_luaL_addvalue (ml_luaL_Buffer *B);
	void ml_luaL_argcheck (ml_lua_State *L, int cond, int narg, String extramsg);
	int  ml_luaL_argerror (ml_lua_State *L, int narg, String extramsg);
	void ml_luaL_buffinit (ml_lua_State *L, ml_luaL_Buffer *B);
	int  ml_luaL_callmeta (ml_lua_State *L, int obj, String e);
	void ml_luaL_checkany (ml_lua_State *L, int narg);
	int  ml_luaL_checkint (ml_lua_State *L, int narg);
	int  ml_luaL_checkinteger (ml_lua_State *L, int narg);
	int  ml_luaL_checklong (ml_lua_State *L, int narg);
	// checklstring -- needs a pointer and is pointless in Monkey
	float ml_luaL_checknumber (ml_lua_State *L, int narg);
	// checkoption -- maybe in the future
	void ml_luaL_checkstack (ml_lua_State *L, int sz, String msg);
	String ml_luaL_checkstring (ml_lua_State *L, int narg);
	void ml_luaL_checktype (ml_lua_State *L, int narg, int t);
	ml_Pointer *ml_luaL_checkudata (ml_lua_State *L, int narg, String tname);
	int  ml_luaL_dofile (ml_lua_State *L, String filename);
	int  ml_luaL_dostring(ml_lua_State *L, String str);
	int  ml_luaL_error(ml_lua_State *L, int argcount, String fmt,
		String arg1, String arg2, String arg3, String arg4, String arg5, String arg6, String arg7, String arg8, String arg9, String arg10);
	int  ml_luaL_getmetafield (ml_lua_State *L, int obj, String e);
	void ml_luaL_getmetatable (ml_lua_State *L, String tname);
	String ml_luaL_gsub (ml_lua_State *L, String s, String p, String r);
	int  ml_luaL_loadbuffer (ml_lua_State *L, Array<int> buff, int sz, String name);
	int  ml_luaL_loadfile (ml_lua_State *L, String filename);
	int  ml_luaL_loadstring (ml_lua_State *L, String s);
	int  ml_luaL_newmetatable (ml_lua_State *L, String tname);
	ml_lua_State *ml_luaL_newstate_rv(ml_lua_State *rv);
	ml_lua_State *ml_luaL_newstate();
	void ml_luaL_openlibs(ml_lua_State *L);
	int  ml_luaL_optint (ml_lua_State *L, int narg, int d);
	int  ml_luaL_optinteger (ml_lua_State *L, int narg, int d);
	int  ml_luaL_optlong (ml_lua_State *L, int narg, int d);
	// optlstring -- needs a pointer and is pointless in Monkey
	float ml_luaL_optnumber (ml_lua_State *L, int narg, float d);
	String ml_luaL_optstring (ml_lua_State *L, int narg, String d);
	// prepbuffer -- requires lua_Buffer
	// pushresult -- requires lua_Buffer
	int  ml_luaL_ref (ml_lua_State *L, int t);
	// register -- requires luaL_Reg
	String ml_luaL_typename (ml_lua_State *L, int index);
	int  ml_luaL_typerror (ml_lua_State *L, int narg, String tname);
	void ml_luaL_unref (ml_lua_State *L, int t, int ref);
	void ml_luaL_where (ml_lua_State *L, int lvl);
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	bool ml_IsMonkeyObject(ml_lua_State *L, int idx);
	void ml_PushMonkeyObject(ml_lua_State *L, Object *obj);
	void ml_PushMonkeyFunction(ml_lua_State *L, ml_MonkeyFunction *func);
	Object *ml_ToMonkeyObject(ml_lua_State *L, int index);
	ml_MonkeyFunction *ml_ToMonkeyFunction(ml_lua_State *L, int index);
	static int ml_CallMonkeyFunction(lua_State *L);
	
	void ml_SetMLI(Object *mli);
	void ml_Init(ml_lua_State *L);
	
	virtual void ml_Retain(Object *obj) {}
	virtual void ml_Release(Object *obj) {}
	virtual int ml_GetField(ml_lua_State *L) { return 0; }
	virtual int ml_SetField(ml_lua_State *L) { return 0; }
	//virtual String ml_GetField(Object *obj, String fieldname) { return String(); }
	//virtual void ml_SetField(Object *obj, String fieldname, String value) {}
	static int ml_Collect(lua_State *L);
	static int ml_GetFieldCallback(lua_State *L);
	static int ml_SetFieldCallback(lua_State *L);
};

ml_MonkeyLua *MLI;

void ml_MonkeyLua::ml_SetMLI(Object *mli)
{
	MLI = (ml_MonkeyLua *)mli;
}

// void lua_call (lua_State *L, int nargs, int nresults);
void ml_MonkeyLua::ml_lua_call (ml_lua_State *L, int nargs, int nresults)
{
	lua_call(L->_data, nargs, nresults);
}

// int lua_checkstack (lua_State *L, int extra);
int ml_MonkeyLua::ml_lua_checkstack (ml_lua_State *L, int extra)
{
	return lua_checkstack(L->_data, extra);
}

// void lua_close (lua_State *L);
void ml_MonkeyLua::ml_lua_close(ml_lua_State *L)
{
	lua_close(L->_data);
	// TODO: should we delete L here?
}

// void lua_concat (lua_State *L, int n);
void ml_MonkeyLua::ml_lua_concat (ml_lua_State *L, int n)
{
	lua_concat(L->_data, n);
}

// TODO: cpcall

// void lua_createtable (lua_State *L, int narr, int nrec);
void ml_MonkeyLua::ml_lua_createtable (ml_lua_State *L, int narr, int nrec)
{
	lua_createtable(L->_data, narr, nrec);
}

// TODO: dump

// int lua_equal (lua_State *L, int index1, int index2);
int ml_MonkeyLua::ml_lua_equal (ml_lua_State *L, int index1, int index2)
{
	return lua_equal(L->_data, index1, index2);
}

// int lua_error (lua_State *L);
int ml_MonkeyLua::ml_lua_error (ml_lua_State *L)
{
	return lua_error(L->_data);
}

// int lua_gc (lua_State *L, int what, int data);
int ml_MonkeyLua::ml_lua_gc (ml_lua_State *L, int what, int data)
{
	return lua_gc(L->_data, what, data);
}

// lua_Alloc lua_getallocf (lua_State *L, void **ud);
ml_lua_Alloc *ml_MonkeyLua::ml_lua_getallocf_rv (ml_lua_State *L, ml_Pointer *ud, ml_lua_Alloc *rv)
{
	rv->_data = lua_getallocf(L->_data, (void **)ud->_voidstar);
	return rv;
}
ml_lua_Alloc *ml_MonkeyLua::ml_lua_getallocf (ml_lua_State *L, ml_Pointer *ud)
{
	return ml_MonkeyLua::ml_lua_getallocf_rv(L, ud, new ml_lua_Alloc()); // FIXME: no delete yet!!!
}

// void lua_getfenv (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_getfenv (ml_lua_State *L, int index)
{
	lua_getfenv(L->_data, index);
}

// void lua_getfield (lua_State *L, int index, const char *k);
void ml_MonkeyLua::ml_lua_getfield (ml_lua_State *L, int index, String k)
{
	lua_getfield(L->_data, index, k.ToCString<char>());
}

// void lua_getglobal (lua_State *L, const char *name);
void ml_MonkeyLua::ml_lua_getglobal (ml_lua_State *L, String name)
{
	lua_getglobal(L->_data, name.ToCString<char>());
}

// TODO: gethook

// TODO: gethookcount

// TODO: gethookmask

// TODO: getinfo

// TODO: getlocal

// int lua_getmetatable (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_getmetatable (ml_lua_State *L, int index)
{
	return lua_getmetatable(L->_data, index);
}

// TODO: getstack

// void lua_gettable (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_gettable (ml_lua_State *L, int index) {
	lua_gettable(L->_data, index);
}

// int lua_gettop (lua_State *L);
int ml_MonkeyLua::ml_lua_gettop (ml_lua_State *L) {
	return lua_gettop(L->_data);
}

// const char *lua_getupvalue (lua_State *L, int funcindex, int n);
String ml_MonkeyLua::ml_lua_getupvalue (ml_lua_State *L, int funcindex, int n) {
	const char *rv = lua_getupvalue(L->_data, funcindex, n);
	return String(rv);
}

// void lua_insert (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_insert (ml_lua_State *L, int index) {
	lua_insert(L->_data, index);
}

// int lua_isboolean (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isboolean (ml_lua_State *L, int index) {
	return lua_isboolean(L->_data, index);
}

// int lua_iscfunction (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_iscfunction (ml_lua_State *L, int index) {
	return lua_iscfunction(L->_data, index);
}

// int lua_isfunction (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isfunction (ml_lua_State *L, int index) {
	return lua_isfunction(L->_data, index);
}

// int lua_islightuserdata (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_islightuserdata(ml_lua_State *L, int index) {
	return lua_islightuserdata(L->_data, index);
}

// int lua_isnil (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isnil (ml_lua_State *L, int index) {
	return lua_isnil(L->_data, index);
}

// int lua_isnone (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isnone (ml_lua_State *L, int index) {
	return lua_isnone(L->_data, index);
}

// int lua_isnoneornil (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isnoneornil (ml_lua_State *L, int index) {
	return lua_isnoneornil(L->_data, index);
}

// int lua_isnumber (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isnumber (ml_lua_State *L, int index) {
	return lua_isnumber(L->_data, index);
}

// int lua_isstring (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isstring (ml_lua_State *L, int index) {
	return lua_isstring(L->_data, index);
}

// int lua_istable (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_istable (ml_lua_State *L, int index) {
	return lua_istable(L->_data, index);
}

// int lua_isthread (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isthread (ml_lua_State *L, int index) {
	return lua_isthread(L->_data, index);
}

// int lua_isuserdata (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_isuserdata (ml_lua_State *L, int index) {
	return lua_isuserdata(L->_data, index);
}

// int lua_lessthan (lua_State *L, int index1, int index2);
int ml_MonkeyLua::ml_lua_lessthan (ml_lua_State *L, int index1, int index2) {
	return lua_lessthan(L->_data, index1, index2);
}

// TODO: load

// lua_State *lua_newstate (lua_Alloc f, void *ud);
ml_lua_State *ml_MonkeyLua::ml_lua_newstate_rv(ml_lua_Alloc *f, ml_Pointer *ud, ml_lua_State *rv) {
	void *ptr = NULL;
	if(ud) ptr = ud->_voidstar;
	rv->_data = lua_newstate(f->_data, ptr);
	return rv;
}
ml_lua_State *ml_MonkeyLua::ml_lua_newstate(ml_lua_Alloc *f, ml_Pointer *ud) {
	return this->ml_lua_newstate_rv(f, ud, new ml_lua_State());
}

//void lua_newtable (lua_State *L);
void ml_MonkeyLua::ml_lua_newtable (ml_lua_State *L) {
	lua_newtable(L->_data);
}

// lua_State *lua_newthread (lua_State *L);
ml_lua_State *ml_MonkeyLua::ml_lua_newthread_rv (ml_lua_State *L, ml_lua_State *rv) {
	rv->_data = lua_newthread(L->_data);
	return rv;
}
ml_lua_State *ml_MonkeyLua::ml_lua_newthread (ml_lua_State *L) {
	return this->ml_lua_newthread_rv (L, new ml_lua_State());
}

// void *lua_newuserdata (lua_State *L, size_t size);
ml_Pointer *ml_MonkeyLua::ml_lua_newuserdata_rv (ml_lua_State *L, int size, ml_Pointer *rv) {
	rv->_voidstar = lua_newuserdata(L->_data, (size_t)size);
	return rv;
}
ml_Pointer *ml_MonkeyLua::ml_lua_newuserdata (ml_lua_State *L, int size) {
	return ml_lua_newuserdata_rv(L, (size_t)size, new ml_Pointer());
}

// int lua_next (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_next (ml_lua_State *L, int index) {
	return lua_next(L->_data, index);
}

// size_t lua_objlen (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_objlen (ml_lua_State *L, int index) {
	return lua_objlen(L->_data, index);
}

// int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);
int ml_MonkeyLua::ml_lua_pcall (ml_lua_State *L, int nargs, int nresults, int msgh) {
	return lua_pcall(L->_data, nargs, nresults, msgh);
}

// void lua_pop (lua_State *L, int n);
void ml_MonkeyLua::ml_lua_pop (ml_lua_State *L, int n) {
	lua_pop(L->_data, n);
}

// void lua_pushboolean (lua_State *L, int b);
void ml_MonkeyLua::ml_lua_pushboolean (ml_lua_State *L, int b) {
	lua_pushboolean(L->_data, b);
}

// TODO: pushcclosure

// TODO: pushcfunction

// TODO: const char *lua_pushfstring (lua_State *L, const char *fmt, ...);

// void lua_pushinteger (lua_State *L, lua_Integer n);
void ml_MonkeyLua::ml_lua_pushinteger (ml_lua_State *L, int n) {
	lua_pushinteger(L->_data, (lua_Integer)n);
}

// void lua_pushlightuserdata (lua_State *L, void *p);
void ml_MonkeyLua::ml_lua_pushlightuserdata (ml_lua_State *L, ml_Pointer *p) {
	lua_pushlightuserdata(L->_data, p->_voidstar);
}

// const char *lua_pushliteral (lua_State *L, const char *s); FIXME
String ml_MonkeyLua::ml_lua_pushliteral (ml_lua_State *L, String s) {
	//const char *rv = lua_pushliteral(L->_data, s.ToCString<char>());
	return s;//String(rv);
}

// void lua_pushlstring (lua_State *L, const char *s, size_t len);
void ml_MonkeyLua::ml_lua_pushlstring (ml_lua_State *L, String s, int len) {
	lua_pushlstring(L->_data, s.ToCString<char>(), (size_t)len);
}

// void lua_pushnil (lua_State *L);
void ml_MonkeyLua::ml_lua_pushnil (ml_lua_State *L) {
	lua_pushnil(L->_data);
}

// void lua_pushnumber (lua_State *L, lua_Number n);
void ml_MonkeyLua::ml_lua_pushnumber (ml_lua_State *L, float n) {
	lua_pushnumber(L->_data, (lua_Number)n);
}

// void lua_pushstring (lua_State *L, const char *s);
void ml_MonkeyLua::ml_lua_pushstring (ml_lua_State *L, String s) {
	lua_pushstring(L->_data, s.ToCString<char>());
}

// int lua_pushthread (lua_State *L);
int ml_MonkeyLua::ml_lua_pushthread (ml_lua_State *L) {
	return lua_pushthread(L->_data);
}

// void lua_pushvalue (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_pushvalue (ml_lua_State *L, int index) {
	lua_pushvalue(L->_data, index);
}

// TODO: const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);

// int lua_rawequal (lua_State *L, int index1, int index2);
int ml_MonkeyLua::ml_lua_rawequal (ml_lua_State *L, int index1, int index2) {
	return lua_rawequal(L->_data, index1, index2);
}

// void lua_rawget (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_rawget (ml_lua_State *L, int index) {
	lua_rawget(L->_data, index);
}

// void lua_rawgeti (lua_State *L, int index, int n);
void ml_MonkeyLua::ml_lua_rawgeti (ml_lua_State *L, int index, int n) {
	lua_rawgeti(L->_data, index, n);
}

// void lua_rawset (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_rawset (ml_lua_State *L, int index) {
	lua_rawset(L->_data, index);
}

// void lua_rawseti (lua_State *L, int index, int n);
void ml_MonkeyLua::ml_lua_rawseti (ml_lua_State *L, int index, int n) {
	lua_rawseti(L->_data, index, n);
}

// TODO: register

// void lua_remove (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_remove (ml_lua_State *L, int index) {
	lua_remove(L->_data, index);
}

// void lua_replace (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_replace (ml_lua_State *L, int index) {
	lua_replace(L->_data, index);
}

// int lua_resume (lua_State *L, int narg);
int ml_MonkeyLua::ml_lua_resume (ml_lua_State *L, int narg) {
	return lua_resume(L->_data, narg);
}

// void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
void ml_MonkeyLua::ml_lua_setallocf (ml_lua_State *L, ml_lua_Alloc *f, ml_Pointer *ud) {
	lua_setallocf(L->_data, f->_data, ud->_voidstar);
}

// int lua_setfenv (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_setfenv (ml_lua_State *L, int index) {
	return lua_setfenv(L->_data, index);
}

// void lua_setfield (lua_State *L, int index, const char *k);
void ml_MonkeyLua::ml_lua_setfield (ml_lua_State *L, int index, String k) {
	lua_setfield(L->_data, index, k.ToCString<char>());
}

// void lua_setglobal (lua_State *L, const char *name);
void ml_MonkeyLua::ml_lua_setglobal (ml_lua_State *L, String name) {
	lua_setglobal(L->_data, name.ToCString<char>());
}

// TODO: sethook

// TODO: setlocal

// void lua_setmetatable (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_setmetatable (ml_lua_State *L, int index) {
	lua_setmetatable(L->_data, index);
}

// void lua_settable (lua_State *L, int index);
void ml_MonkeyLua::ml_lua_settable (ml_lua_State *L, int index) {
	lua_settable(L->_data, index);
}

// void lua_settop (lua_State *L, int n);
void ml_MonkeyLua::ml_lua_settop (ml_lua_State *L, int n) {
	lua_settop(L->_data, n);
}

// const char *lua_setupvalue (lua_State *L, int funcindex, int n);
String ml_MonkeyLua::ml_lua_setupvalue (ml_lua_State *L, int funcindex, int n) {
	const char *rv = lua_setupvalue(L->_data, funcindex, n);
	return String(rv);
}

// int lua_status (lua_State *L);
int ml_MonkeyLua::ml_lua_status (ml_lua_State *L) {
	return lua_status(L->_data);
}

// int lua_toboolean (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_toboolean (ml_lua_State *L, int index) {
	return lua_toboolean(L->_data, index);
}

// TODO: tocfunction

// lua_Integer lua_tointeger (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_tointeger (ml_lua_State *L, int index) {
	return (int)lua_tointeger(L->_data, index);
}

// TODO: const char *lua_tolstring (lua_State *L, int index, size_t *len);
//String ml_lua_tolstring (ml_lua_State *L, int index, int *len)

// lua_Number lua_tonumber (lua_State *L, int index);
float ml_MonkeyLua::ml_lua_tonumber (ml_lua_State *L, int index) {
	return (float)lua_tonumber(L->_data, index);
}

// const void *lua_topointer (lua_State *L, int index);
ml_Pointer *ml_MonkeyLua::ml_lua_topointer (ml_lua_State *L, int index) {
	const void *rv = lua_topointer(L->_data, index);
	return new ml_Pointer(rv, 0); // we use a constructor with a dummy argument
}

// const char *lua_tostring (lua_State *L, int index);
String ml_MonkeyLua::ml_lua_tostring (ml_lua_State *L, int index) {
	const char *rv = lua_tostring(L->_data, index);
	return String(rv);
}

// lua_State *lua_tothread (lua_State *L, int index);
ml_lua_State *ml_MonkeyLua::ml_lua_tothread_rv (ml_lua_State *L, int index, ml_lua_State *rv) {
	rv->_data = lua_tothread(L->_data, index);
	return rv;
}
ml_lua_State *ml_MonkeyLua::ml_lua_tothread (ml_lua_State *L, int index) {
	return this->ml_lua_tothread_rv(L, index, new ml_lua_State());
}

// int lua_type (lua_State *L, int index);
int ml_MonkeyLua::ml_lua_type (ml_lua_State *L, int index) {
	return lua_type(L->_data, index);
}

// const char *lua_typename  (lua_State *L, int tp);
String ml_MonkeyLua::ml_lua_typename (ml_lua_State *L, int tp) {
	const char *rv = lua_typename(L->_data, tp);
	return String(rv);
}

// void lua_xmove (lua_State *from, lua_State *to, int n);
void ml_MonkeyLua::ml_lua_xmove (ml_lua_State *from, ml_lua_State *to, int n) {
	lua_xmove(from->_data, to->_data, n);
}

// int lua_yield (lua_State *L, int nresults);
int ml_MonkeyLua::ml_lua_yield (ml_lua_State *L, int nresults) {
	return lua_yield(L->_data, nresults);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// void luaL_addchar (luaL_Buffer *B, char c);
void ml_MonkeyLua::ml_luaL_addchar (ml_luaL_Buffer *B, int c) {
	luaL_addchar(B->_data, (char)c);
}

// void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
void ml_MonkeyLua::ml_luaL_addlstring (ml_luaL_Buffer *B, String s, int l) {
	luaL_addlstring(B->_data, s.ToCString<char>(), (size_t)l);
}

// void luaL_addsize (luaL_Buffer *B, size_t n);
void ml_MonkeyLua::ml_luaL_addsize (ml_luaL_Buffer *B, int n) {
	luaL_addsize(B->_data, (size_t)n);
}

// void luaL_addstring (luaL_Buffer *B, const char *s);
void ml_MonkeyLua::ml_luaL_addstring (ml_luaL_Buffer *B, String s) {
	luaL_addstring(B->_data, s.ToCString<char>());
}

// void luaL_addvalue (luaL_Buffer *B);
void ml_MonkeyLua::ml_luaL_addvalue (ml_luaL_Buffer *B) {
	luaL_addvalue(B->_data);
}

// void luaL_argcheck (lua_State *L, int cond, int narg, const char *extramsg);
void ml_MonkeyLua::ml_luaL_argcheck (ml_lua_State *L, int cond, int narg, String extramsg) {
	luaL_argcheck(L->_data, cond, narg, extramsg.ToCString<char>());
}

// int luaL_argerror (lua_State *L, int narg, const char *extramsg);
int ml_MonkeyLua::ml_luaL_argerror (ml_lua_State *L, int narg, String extramsg) {
	return luaL_argerror(L->_data, narg, extramsg.ToCString<char>());
}

// void luaL_buffinit (lua_State *L, luaL_Buffer *B);
void ml_MonkeyLua::ml_luaL_buffinit (ml_lua_State *L, ml_luaL_Buffer *B) {
	luaL_buffinit(L->_data, B->_data);
}

// TODO: char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);

// int luaL_callmeta (lua_State *L, int obj, const char *e);
int ml_MonkeyLua::ml_luaL_callmeta (ml_lua_State *L, int obj, String e) {
	return luaL_callmeta(L->_data, obj, e.ToCString<char>());
}

// void luaL_checkany (lua_State *L, int narg);
void ml_MonkeyLua::ml_luaL_checkany (ml_lua_State *L, int narg) {
	luaL_checkany(L->_data, narg);
}

// int luaL_checkint (lua_State *L, int narg);
int ml_MonkeyLua::ml_luaL_checkint (ml_lua_State *L, int narg) {
	return luaL_checkint(L->_data, narg);
}

// lua_Integer luaL_checkinteger (lua_State *L, int narg);
int ml_MonkeyLua::ml_luaL_checkinteger (ml_lua_State *L, int narg) {
	return (int)luaL_checkinteger(L->_data, narg);
}

// long luaL_checklong (lua_State *L, int narg);
int ml_MonkeyLua::ml_luaL_checklong (ml_lua_State *L, int narg) {
	return (int)luaL_checklong(L->_data, narg);
}

// TODO: const char *luaL_checklstring (lua_State *L, int narg, size_t *l);

// lua_Number luaL_checknumber (lua_State *L, int narg);
float ml_MonkeyLua::ml_luaL_checknumber (ml_lua_State *L, int narg) {
	return (float)luaL_checknumber(L->_data, narg);
}

// TODO: int luaL_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]);

// void luaL_checkstack (lua_State *L, int sz, const char *msg);
void ml_MonkeyLua::ml_luaL_checkstack (ml_lua_State *L, int sz, String msg) {
	luaL_checkstack(L->_data, sz, msg.ToCString<char>());
}

// const char *luaL_checkstring (lua_State *L, int narg);
String ml_MonkeyLua::ml_luaL_checkstring (ml_lua_State *L, int narg) {
	const char *rv = luaL_checkstring(L->_data, narg);
	return String(rv);
}

// void luaL_checktype (lua_State *L, int narg, int t);
void ml_MonkeyLua::ml_luaL_checktype (ml_lua_State *L, int narg, int t) {
	luaL_checktype(L->_data, narg, t);
}

// void *luaL_checkudata (lua_State *L, int narg, const char *tname);
ml_Pointer *ml_MonkeyLua::ml_luaL_checkudata (ml_lua_State *L, int narg, String tname) {
	void *rv = luaL_checkudata(L->_data, narg, tname.ToCString<char>());
	return new ml_Pointer(rv);
}

// int luaL_dofile (lua_State *L, const char *filename);
int ml_MonkeyLua::ml_luaL_dofile (ml_lua_State *L, String filename) {
	return luaL_dofile(L->_data, filename.ToCString<char>());
}

// int luaL_dostring (lua_State *L, const char *str);
int ml_MonkeyLua::ml_luaL_dostring(ml_lua_State *L, String str) {
	return luaL_dostring(L->_data, str.ToCString<char>());
}

// int luaL_error (lua_State *L, const char *fmt, ...);
int ml_MonkeyLua::ml_luaL_error(ml_lua_State *L, int argcount, String fmt,
	String arg1, String arg2, String arg3, String arg4, String arg5, String arg6, String arg7, String arg8, String arg9, String arg10)
{
	int rv = 0;
	// unfortunately since Monkey doesn't support varargs, we must pass all of these in with an arg count (to be fixed?)
	if(argcount==1)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>());
	else if(argcount==2)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>());
	else if(argcount==3)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>(), arg3.ToCString<char>());
	else if(argcount==4)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>(), arg3.ToCString<char>(), arg4.ToCString<char>());
	else if(argcount==5)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>(), arg3.ToCString<char>(), arg4.ToCString<char>(),
			arg5.ToCString<char>());
	else if(argcount==6)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>(), arg3.ToCString<char>(), arg4.ToCString<char>(),
			arg5.ToCString<char>(), arg6.ToCString<char>());
	else if(argcount==7)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>(), arg3.ToCString<char>(), arg4.ToCString<char>(),
			arg5.ToCString<char>(), arg6.ToCString<char>(), arg7.ToCString<char>());
	else if(argcount==8)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>(), arg3.ToCString<char>(), arg4.ToCString<char>(),
			arg5.ToCString<char>(), arg6.ToCString<char>(), arg7.ToCString<char>(), arg8.ToCString<char>());
	else if(argcount==9)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>(), arg3.ToCString<char>(), arg4.ToCString<char>(),
			arg5.ToCString<char>(), arg6.ToCString<char>(), arg7.ToCString<char>(), arg8.ToCString<char>(),
			arg9.ToCString<char>());
	else if(argcount==10)
		rv = luaL_error(L->_data, fmt.ToCString<char>(), arg1.ToCString<char>(), arg2.ToCString<char>(), arg3.ToCString<char>(), arg4.ToCString<char>(),
			arg5.ToCString<char>(), arg6.ToCString<char>(), arg7.ToCString<char>(), arg8.ToCString<char>(),
			arg9.ToCString<char>(), arg10.ToCString<char>());
	else
		rv = luaL_error(L->_data, fmt.ToCString<char>());
	return rv;
}

// int luaL_getmetafield (lua_State *L, int obj, const char *e);
int ml_MonkeyLua::ml_luaL_getmetafield (ml_lua_State *L, int obj, String e)
{
	return luaL_getmetafield(L->_data, obj, e.ToCString<char>());
}

// void luaL_getmetatable (lua_State *L, const char *tname);
void ml_MonkeyLua::ml_luaL_getmetatable (ml_lua_State *L, String tname)
{
	luaL_getmetatable(L->_data, tname.ToCString<char>());
}

// const char *luaL_gsub (lua_State *L, const char *s, const char *p, const char *r);
String ml_MonkeyLua::ml_luaL_gsub (ml_lua_State *L, String s, String p, String r)
{
	const char *rv = luaL_gsub(L->_data, s.ToCString<char>(), p.ToCString<char>(), r.ToCString<char>());
	return String(rv);
}

// int luaL_loadbuffer (lua_State *L, const char *buff, size_t sz, const char *name);
int ml_MonkeyLua::ml_luaL_loadbuffer (ml_lua_State *L, Array<int> buff, int sz, String name)
{
	char *arr = (char *)malloc((sz+1) * sizeof(char));
	for(int i=0;i<sz;i++) {
		arr[i] = (char)buff[0];
	}
	arr[sz-1] = '\0';
	int rv = luaL_loadbuffer(L->_data, arr, (size_t)sz, name.ToCString<char>());
	free(arr);
	return rv;
}

// int luaL_loadfile (lua_State *L, const char *filename);
int ml_MonkeyLua::ml_luaL_loadfile (ml_lua_State *L, String filename)
{
	return luaL_loadfile(L->_data, filename.ToCString<char>());
}

// int luaL_loadstring (lua_State *L, const char *s);
int ml_MonkeyLua::ml_luaL_loadstring (ml_lua_State *L, String s)
{
	return luaL_loadstring(L->_data, s.ToCString<char>());
}

// int luaL_newmetatable (lua_State *L, const char *tname);
int ml_MonkeyLua::ml_luaL_newmetatable (ml_lua_State *L, String tname)
{
	return luaL_newmetatable(L->_data, tname.ToCString<char>());
}

// lua_State *luaL_newstate (void);
ml_lua_State *ml_MonkeyLua::ml_luaL_newstate_rv(ml_lua_State *rv)
{
	rv->_data = luaL_newstate();
	return rv;
}
ml_lua_State *ml_MonkeyLua::ml_luaL_newstate() {
	return this->ml_luaL_newstate_rv(new ml_lua_State());
}

// void luaL_openlibs (lua_State *L);
void ml_MonkeyLua::ml_luaL_openlibs(ml_lua_State *L)
{
	luaL_openlibs(L->_data);
}

// int luaL_optint (lua_State *L, int narg, int d);
int ml_MonkeyLua::ml_luaL_optint (ml_lua_State *L, int narg, int d)
{
	return luaL_optint(L->_data, narg, d);
}

// lua_Integer luaL_optinteger (lua_State *L, int narg, lua_Integer d);
int ml_MonkeyLua::ml_luaL_optinteger (ml_lua_State *L, int narg, int d)
{
	return (int)luaL_optinteger(L->_data, narg, (lua_Integer)d);
}

// long luaL_optlong (lua_State *L, int narg, long d);
int ml_MonkeyLua::ml_luaL_optlong (ml_lua_State *L, int narg, int d)
{
	return (int)luaL_optlong(L->_data, narg, (long)d);
}

// TODO: const char *luaL_optlstring (lua_State *L, int narg, const char *d, size_t *l);

// lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number d);
float ml_MonkeyLua::ml_luaL_optnumber (ml_lua_State *L, int narg, float d)
{
	return (float)luaL_optnumber(L->_data, narg, (lua_Number)d);
}

// const char *luaL_optstring (lua_State *L, int narg, const char *d);
String ml_MonkeyLua::ml_luaL_optstring (ml_lua_State *L, int narg, String d)
{
	const char *rv = luaL_optstring(L->_data, narg, d.ToCString<char>());
	return String(rv);
}

// TODO: char *luaL_prepbuffer (luaL_Buffer *B);

// TODO: pushresult

// int luaL_ref (lua_State *L, int t);
int ml_MonkeyLua::ml_luaL_ref (ml_lua_State *L, int t)
{
	return luaL_ref(L->_data, t);
}

// TODO: register

// const char *luaL_typename (lua_State *L, int index);
String ml_MonkeyLua::ml_luaL_typename (ml_lua_State *L, int index)
{
	const char *rv = luaL_typename(L->_data, index);
	return String(rv);
}

// int luaL_typerror (lua_State *L, int narg, const char *tname);
int ml_MonkeyLua::ml_luaL_typerror (ml_lua_State *L, int narg, String tname) {
	return luaL_typerror(L->_data, narg, tname.ToCString<char>());
}

// void luaL_unref (lua_State *L, int t, int ref);
void ml_MonkeyLua::ml_luaL_unref (ml_lua_State *L, int t, int ref)
{
	luaL_unref(L->_data, t, ref);
}

// void luaL_where (lua_State *L, int lvl);
void ml_MonkeyLua::ml_luaL_where (ml_lua_State *L, int lvl)
{
	luaL_where(L->_data, lvl);
}

bool ml_MonkeyLua::ml_IsMonkeyObject(ml_lua_State *L, int idx)
{
	if(lua_gettop(L->_data)<idx || lua_isnil(L->_data, idx) || !lua_istable(L->_data, idx))
		return false;
	lua_getmetatable(L->_data, idx);
	luaL_getmetatable(L->_data, MONKEYLUA_OBJECT_MT);
	bool rv = lua_equal(L->_data, -1, -2)==1;
	lua_pop(L->_data, 2);
	return rv;
}

void ml_MonkeyLua::ml_PushMonkeyObject(ml_lua_State *L, Object *obj)
{
	lua_newtable(L->_data);
	lua_pushstring(L->_data, "__pointer");
	lua_pushlightuserdata(L->_data, obj);
	lua_rawset(L->_data, -3);
	luaL_getmetatable(L->_data, MONKEYLUA_OBJECT_MT);
	lua_setmetatable(L->_data, -2);
	MLI->ml_Retain(obj);
}

void ml_MonkeyLua::ml_PushMonkeyFunction(ml_lua_State *L, ml_MonkeyFunction *func)
{
	lua_newtable(L->_data);
	lua_pushstring(L->_data, "__pointer");
	lua_pushlightuserdata(L->_data, func);
	lua_rawset(L->_data, -3);
	luaL_getmetatable(L->_data, MONKEYLUA_OBJECT_MT);
	lua_setmetatable(L->_data, -2);
	MLI->ml_Retain(func);
}

Object *ml_MonkeyLua::ml_ToMonkeyObject(ml_lua_State *L, int index)
{
	if(lua_gettop(L->_data)<1 || lua_isnil(L->_data,1) || !ml_MonkeyLua::ml_IsMonkeyObject(L, index))
	{
		luaL_argerror(L->_data, index, "expected a Monkey Object");
		return NULL;
	}
	lua_pushstring(L->_data, "__pointer");
	lua_rawget(L->_data, index);
	void *ud = lua_touserdata(L->_data, -1);
	lua_pop(L->_data, 1);
	return (Object *)ud;
}

ml_MonkeyFunction *ml_MonkeyLua::ml_ToMonkeyFunction(ml_lua_State *L, int index)
{
	if(lua_gettop(L->_data)<1 || lua_isnil(L->_data,1) || !ml_MonkeyLua::ml_IsMonkeyObject(L, index))
	{
		luaL_argerror(L->_data, index, "expected a Monkey Function");
		return NULL;
	}
	lua_pushstring(L->_data, "__pointer");
	lua_rawget(L->_data, index);
	void *ud = lua_touserdata(L->_data, -1);
	lua_pop(L->_data, 1);
	return (ml_MonkeyFunction *)ud;
}

// this is __call in the object metatable
int ml_MonkeyLua::ml_CallMonkeyFunction(lua_State *L)
{
	// should have at least one parameter (the monkey function) as a light userdata
	lua_pushstring(L, "__pointer");
	lua_rawget(L, 1);
	void *ud = lua_touserdata(L, -1);
	lua_pop(L, 1);
	ml_MonkeyFunction *mf = (ml_MonkeyFunction *)ud;
	
	// remove the monkey function from the stack, because we only want to pass the parameters
	lua_remove(L, 1);
	// call the function and keep the return value
	ml_lua_State state(L);
	int returncount = mf->ml_Func(&state);
	return returncount;
}

// this is __index in the object metatable
int ml_MonkeyLua::ml_GetFieldCallback(lua_State *L)
{
	ml_lua_State state(L);
	return MLI->ml_GetField(&state);
}

// this is __newindex in the object metatable
int ml_MonkeyLua::ml_SetFieldCallback(lua_State *L)
{
	ml_lua_State state(L);
	return MLI->ml_SetField(&state);
}

// this is __gc in the object metatable
int ml_MonkeyLua::ml_Collect(lua_State *L)
{
	// first param should be the object
	lua_pushstring(L, "__pointer");
	lua_rawget(L, 1);
	void *ud = lua_touserdata(L, -1);
	lua_pop(L, 1);
	Object *o = (Object *)ud;
	// call release
	MLI->ml_Release(o);
	// we return no values
	return 0;
}

void ml_MonkeyLua::ml_Init(ml_lua_State *L)
{
	// create the metatable for objects and functions
	luaL_newmetatable(L->_data, MONKEYLUA_OBJECT_MT);
	// __call
	lua_pushstring(L->_data, "__call");
	lua_pushcfunction(L->_data, ml_MonkeyLua::ml_CallMonkeyFunction);
	lua_rawset(L->_data, -3);
	// __index
	lua_pushstring(L->_data, "__index");
	lua_pushcfunction(L->_data, ml_MonkeyLua::ml_GetFieldCallback);
	lua_rawset(L->_data, -3);
	// __newindex
	lua_pushstring(L->_data, "__newindex");
	lua_pushcfunction(L->_data, ml_MonkeyLua::ml_SetFieldCallback);
	lua_rawset(L->_data, -3);
	// __gc
	lua_pushstring(L->_data, "__gc");
	lua_pushcfunction(L->_data, ml_MonkeyLua::ml_Collect);
	lua_rawset(L->_data, -3);
	// pop the metatable
	lua_pop(L->_data, 1);
}
